package main

import "fmt"

type treeNode struct {
	value       int
	left, right *treeNode // 左指针，右指针
}

func main() {
	var root treeNode // {0 <nil> <nil>}

	// struct 初始化方式
	root = treeNode{value: 3}
	root.left = &treeNode{}
	root.right = &treeNode{5, nil, nil}
	root.right.left = new(treeNode)
	root.left.right = createNode(2)

	// slice treeNode
	nodes := []treeNode{
		{value: 3},
		{},
		{6, nil, nil},
	}

	fmt.Println("root::", root)   // {3 0xc000004078 0xc000004090}
	fmt.Println("nodes::", nodes) // [{3 <nil> <nil>} {0 <nil> <nil>} {6 <nil> <nil>}]

	// 访问
	// 不论地址还是结构本身，一律使用 . 来访问成员

	// 没有构造函数

	// 结构创建在 栈上还是 堆上
	// C++
	// 局部变量分配在 栈上的，局部变量一旦退出 就会销毁
	// 如果局部变量传出去，必须在堆上分配，并且需要手动释放
	// java
	// 几乎所有东西都存放在 堆上的 （new）所以才会有 垃圾回收机制
	// go
	// 不需要知道，由 编译器与运行环境自动决定
	// 如果局部变量 没有取地址 返回出去，并不需要给别人用，编译器，就可能在栈上分配
	// 如果局部变量 取地址 返回出去 并给别人用了，编译器 ，就可能在栈堆分配，并参与垃圾回收

	// 为结构定义方法

	root.right.left.setValue(4) //相当于 setValue(&root.right.left,4)
	root.right.left.print()     //相当于 print(root.right.left) 拷贝一份 root 给 print()

	// nil 指针也可以调用方法

	var pRoot *treeNode
	pRoot.setValue(200) // nil.value 会报错
	pRoot = &root
	pRoot.setValue(300)
	pRoot.print()
	// 遍历
	root.traverse()

	// 值接收者与指针接收者
	// 要改变内容必须使用指针接收者
	// 结构过大也考虑使用指针接受者
	// 一致性：如果有指针接受者，最好都是指针接收者，避免歧义
	// 值接收者是 go 特有的
	// 值/指针接收者 都可以 接收值/指针 ，不会改变方法调用者的调用方式
}

// 工厂函数，场景：自己控制构造

func createNode(value int) *treeNode {
	// 相当于定义了一个局部变量，返回了局部变量的地址
	// 注意：返回的是局部变量的地址
	// c++ 中不能返回局部变量
	return &treeNode{value: value}
}

// 为结构定义方法
// (node treeNode) 接收者 相当于 其他语言中的 this
// node 是 传的一个值类型
// 只是调用方式不同
func (node treeNode) print() {
	fmt.Println(node.value)
}

func (node *treeNode) setValue(value int) {
	if node == nil {
		fmt.Println("Setting value to nil node. Ignored.")
		return
	}
	node.value = value
}

// 遍历 中序遍历 ， 先遍历左子树
func (node *treeNode) traverse() {

	if node == nil {
		return
	}

	node.left.traverse()
	node.print()
	node.right.traverse()
}
